unit logging;

{$mode objfpc}{$H+}

interface

procedure Log(const S: String);
procedure Log(const I: UInt64);
procedure Log(const S: String; const i: UInt64);
procedure Log(const S: String; Params: array of const);

{ Log the last exception and also produce a stack trace }
procedure LogException(const S: String; Params: array of const);
procedure LogException(const S: String);
procedure LogException();

implementation
uses
  Classes, SysUtils;

var
  CriticalSection: TRTLCriticalSection;

procedure Log(const S: String);
begin
  if IsConsole then
    writeln(S);
end;

procedure Log(const I: UInt64);
begin
  Log('%s (%d)', [IntToHex(I, 16), I]);
end;

procedure Log(const S: String; const i: UInt64);
begin
  Log('%s: %s (%d)', [S, IntToHex(i, 16), i]);
end;

procedure Log(const S: String; Params: array of const);
begin
  try
    Log(Format(S, Params));
  except
    Log(S + ' <-- error while formatting the messsage');
  end;
end;

procedure LogException(const S: String; Params: array of const);
begin
  LogException((Format(S, Params)));
end;

procedure LogException(const S: String);
var
  E            : Exception;
  FrameNumber,
  FrameCount   : LongInt;
  Frames       : PPointer;
begin
  EnterCriticalsection(CriticalSection);
  if RaiseList <> nil then begin;
    E := Exception(RaiseList^.FObject);
    log('*** %s: %s: %s', [S, E.ToString, E.Message]);
    Log(BackTraceStrFunc(RaiseList^.Addr));
    FrameCount:=RaiseList^.Framecount;
    Frames:=RaiseList^.Frames;
    for FrameNumber := 0 to FrameCount-1 do
      Log(BackTraceStrFunc(Frames[FrameNumber]));
  end;
  LeaveCriticalsection(CriticalSection);
end;

procedure LogException();
begin
  LogException('Exception');
end;

initialization
  InitCriticalSection(CriticalSection);
finalization
  DoneCriticalsection(CriticalSection);
end.
